package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.common.utils.RequestUtils;
import com.atguigu.gmall.feignclients.product.SkuFeginClient;
import com.atguigu.gmall.product.entity.SkuInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartserviceImpl implements CartService {


    @Autowired
    SkuFeginClient skuFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;


    /**
     * 决定用哪个购物车的键
     * 1、如果只带了tempid；就是临时购物车
     * 2、如果只带了token；就是用户购物车
     * 3、如果两个都带了，优先以用户购物车为准; 而且如果临时购物车还有商品，还要合并到用户购物车中
     *
     * @return
     */
    @Override
    public String determinCartKey() {
        String cartKey = RedisConst.CART_INFO;
        Long uid = RequestUtils.uid();

        //临时id和用户id都有,优先以用户购物车为准
        //1、如果有用户id，一定先用用户的
        if (uid != null) {
            cartKey = cartKey + uid;
            return cartKey;
        }
        //2、是否有临时的
        String tempId = RequestUtils.userTempId();
        if (!StringUtils.isEmpty(tempId)) {
            cartKey = cartKey + tempId;
        }
        return cartKey;
    }

    /**
     * 给购物车添加商品
     *
     * @param skuId
     * @param skuNum
     * @param cartKey
     * @return
     */
    @Override
    public SkuInfo addItemToCart(Long skuId, Integer skuNum, String cartKey) {
        //给购物车中添加商品。 如果购物车之前没有这个商品就是新增。如果有就是修改数量
        //1、以后操作redis都是string， key hashkey hashvalue 都是字符串
        Boolean hasKey = redisTemplate.opsForHash().hasKey(cartKey, skuId.toString());
        SkuInfo data = null;
        if (!hasKey) {
            //TODO --新增要做前置校验，总条目不能超过200个
            Long size = redisTemplate.opsForHash().size(cartKey);
            if (size > RedisConst.CART_ITEM_LIMIT) {
                //购物车条目超限； 超最大限制抛出异常。
                throw new GmallException(ResultCodeEnum.CART_ITEM_OVERFLOW);
            }
            //2、购物车没有这个商品就是新增
            CartInfo cartInfo = new CartInfo();
            Result<SkuInfo> skuInfo = skuFeignClient.getSkuInfo(skuId);
            data = skuInfo.getData();
            cartInfo.setSkuId(skuId);
            cartInfo.setCartPrice(data.getPrice());
            cartInfo.setSkuPrice(data.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setImgUrl(data.getSkuDefaultImg());
            cartInfo.setSkuName(data.getSkuName());
            cartInfo.setIsChecked(1);
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());
            //3、新增
            saveCartItem(cartKey, cartInfo);

        } else {
            //4、购物车有这个商品，只需要修改数量
            CartInfo cartInfo = getCartItem(skuId, cartKey);
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
            //4.3、保存到redis中
            saveCartItem(cartKey, cartInfo);
            String skuName = cartInfo.getSkuName();
            String imgUrl = cartInfo.getImgUrl();
            SkuInfo skuInfo = new SkuInfo();
            skuInfo.setSkuName(skuName);
            skuInfo.setSkuDefaultImg(imgUrl);
            data = skuInfo;
        }

        //4、给临时购物车给一个过期时间
        if (RequestUtils.uid() == null && !StringUtils.isEmpty(RequestUtils.userTempId())) {
            //续期代码，只要使用过一次，都会续满一年
            //redisTemplate.expire(cartKey, Duration.ofDays(365));
            //不续期代码
            Long expire = redisTemplate.getExpire(cartKey);
            if (expire < 0) { //没设置过过期时间就设置
                redisTemplate.expire(cartKey, Duration.ofDays(365));
            }
        }

        return data;
    }

    /**
     * 获取购物车中的商品信息集合
     * @param cartKey
     * @return
     */
    @Override
    public List<CartInfo> getCartList(String cartKey) {
        //如果已经登录,临时购物车的东西,要合并购物车。
        //把临时购物车的所有商品添加到用户登录的购物车中
        Long uid = RequestUtils.uid();
        if (uid != null) {
            String tempId = RequestUtils.userTempId();
            //2、如果临时购物车有数据
            List<Object> tempJson = redisTemplate.opsForHash()
                    .values(RedisConst.CART_INFO + tempId);
            //说明临时购物车有东西
            if (tempJson != null && tempJson.size() > 0) {
                tempJson.stream().forEach(item -> {
                    //临时购物车的当前商品
                    CartInfo info = Jsons.toObj(item.toString(), CartInfo.class);
                    //进行合并
                    addItemToCart(info.getSkuId(), info.getSkuNum(), cartKey);
                });
                //合并后删除临时购物车数据
                redisTemplate.delete(RedisConst.CART_INFO + tempId);
            }
        }

        //先合并完拿到购物车中的所有数据
        List<Object> values = redisTemplate.opsForHash().values(cartKey);


        List<CartInfo> infos = values.stream()
                .map(item -> Jsons.toObj(item.toString(), CartInfo.class))  //
                .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime())) //按照时间排序
                .collect(Collectors.toList()); //终结操作
            //修改价格
            log.info("正在同步购物车价格...");
            batchUpdatePrice(cartKey, infos);

        return infos;
    }

    /**
     * 修改购物车中指定商品的数量
     * @param cartKey
     * @param skuId
     * @param num
     */
    @Override
    public void updateItemCount(String cartKey, Long skuId, Integer num) {
        //1、拿到这个商品
        CartInfo cartInfo = getCartItem(skuId, cartKey);
        //2、修改数量
        cartInfo.setSkuNum(cartInfo.getSkuNum() + num);

        //3、给redis中再一保存
        saveCartItem(cartKey,cartInfo);
    }

    /**
     * 修改购物车中指定商品的状态
     * @param cartKey
     * @param skuId
     * @param status
     */
    @Override
    public void updateItemCheckStatus(String cartKey, Long skuId, Integer status) {
        //先获取这个商品
        CartInfo cartItem = getCartItem(skuId, cartKey);
        cartItem.setIsChecked(status);
        //修改后在存入redis中
        saveCartItem(cartKey,cartItem);

    }

    /**
     * 删除某个商品
     * @param cartKey
     * @param skuId
     */
    @Override
    public void deleteCartItem(String cartKey, Long skuId) {
        redisTemplate.opsForHash().delete(cartKey,skuId.toString());
    }

    /**
     * 删除所有选中的商品
     * @param cartKey
     */
    @Override
    public void deleteChecked(String cartKey) {
        List<CartInfo> cartList = getCartList(cartKey);

        //拿到选中的所有商品 List<String>
        List<String> skuIds = cartList.stream()
                .filter(cartInfo -> cartInfo.getIsChecked() == 1)//筛选出选中的
                .map(cartInfo -> cartInfo.getSkuId().toString())//拿到商品中的id字符串
                .collect(Collectors.toList());

        //删除  上面筛选出来的id可能为空
        if(skuIds!=null && skuIds.size()>0){
            redisTemplate.opsForHash().delete(cartKey,skuIds.toArray());
        }
    }

    private void batchUpdatePrice(String cartKey, List<CartInfo> infos) {
        List<CartInfo> cartList = infos;

        cartList.stream().forEach(item->{
            Result<BigDecimal> price = skuFeignClient.getSkuPrice(item.getSkuId());
            // 取绝对值
            if(!(Math.abs(price.getData().subtract(item.getSkuPrice()).doubleValue()) < 0.001)){
                log.info("发现【{}】 购物车中 【{}】 商品的价格发生变化，正在同步",cartKey,item.getSkuId());
                item.setSkuPrice(price.getData());
                //同步到redis
                saveCartItem(cartKey,item);
            }
        });
    }

    //从购物车获取某个商品
    private CartInfo getCartItem(Long skuId, String cartKey) {
        //1、拿到购物车中这个商品
        String json = (String) redisTemplate.opsForHash().get(cartKey, skuId.toString());
        //2、逆转成我们能用的对象
        CartInfo cartInfo = Jsons.toObj(json, CartInfo.class);
        return cartInfo;
    }

    //向购物车保存商品
    private void saveCartItem(String cartKey, CartInfo cartInfo) {
        if (cartInfo.getSkuNum() > RedisConst.CART_NUM_LIMIT) {
            throw new GmallException(ResultCodeEnum.CART_ITEM_NUM_OVERFLOW);
        }
        redisTemplate.opsForHash().put(cartKey, cartInfo.getSkuId().toString(), Jsons.toJsonstr(cartInfo));
    }
}
